package com.chu.cloud.service.base;

import com.chu.cloud.dto.AddressDto;
import com.chu.cloud.dto.UserDetail;
import com.chu.cloud.dto.order.BaseOrderCreateDto;
import com.chu.cloud.entity.MerchantOrder;
import com.chu.cloud.entity.OrderDetail;
import com.chu.cloud.enums.OrderStatusEnums;
import com.chu.cloud.enums.OrderType;
import com.chu.cloud.enums.PayStatusEnums;
import com.chu.cloud.enums.RedisKeyPrefix;
import com.chu.cloud.feign.client.IAddressFeignClient;
import com.chu.cloud.rabbit.RabbitConstants;
import com.chu.cloud.rabbit.fanout.send.RabbitFanoutSender;
import com.chu.cloud.repository.MerchantOrderRepository;
import com.chu.cloud.repository.OrderDetailRepository;
import com.chu.cloud.response.ResponseMessage;
import com.chu.cloud.stream.channel.OrderPaySuccessProcessor;
import com.chu.cloud.stream.channel.OrderProductReturnProcessor;
import com.chu.cloud.stream.payload.OrderPaySuccessDto;
import com.chu.cloud.stream.payload.OrderProductReturnDto;
import com.chu.cloud.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author: Tianshu.CHU
 * @Date: 2018/8/24 15:31
 * @Description:
 */
@Slf4j
public abstract class BaseOrderService {

    public static final int ORDER_LENGTH = 24;

    private OrderDetailRepository orderDetailRepository;

    private MerchantOrderRepository orderRepository;

    private OrderProductReturnProcessor orderProductReturnProcessor;

    private RabbitFanoutSender fanoutSender;

    private OrderPaySuccessProcessor orderPaySuccessProcessor;

    @Autowired
    public void setFanoutSender(RabbitFanoutSender fanoutSender) {
        this.fanoutSender = fanoutSender;
    }

    @Autowired
    public void setOrderDetailRepository(OrderDetailRepository orderDetailRepository) {
        this.orderDetailRepository = orderDetailRepository;
    }

    @Autowired
    public void setOrderRepository(MerchantOrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Autowired
    public void setOrderProductReturnProcessor(OrderProductReturnProcessor orderProductReturnProcessor) {
        this.orderProductReturnProcessor = orderProductReturnProcessor;
    }

    @Autowired
    public void setOrderPaySuccessProcessor(OrderPaySuccessProcessor orderPaySuccessProcessor) {
        this.orderPaySuccessProcessor = orderPaySuccessProcessor;
    }

    protected final synchronized String generateOrderNo(OrderType orderType, Integer shopId) {
        String prefix = OrderType.defaulGenerateRule(orderType) + shopId;
        String orderNo = prefix + DateUtils.format(new Date(), DateUtils.YYYYMMDDHHMMSS_SSS);
        if (orderNo.length() < ORDER_LENGTH) {
            orderNo += RandomUtil.getRandNum(ORDER_LENGTH - orderNo.length());
        }
        return orderNo;
    }

    protected OrderDetail packageOrderDetail(Integer userId, String orderNo, Integer shopId, Integer productId, Integer quantity, BigDecimal price) {
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderNo(orderNo);
        orderDetail.setProductId(productId);
        orderDetail.setQuantity(quantity);
        orderDetail.setShopId(shopId);
        orderDetail.setProductPrice(price);
        orderDetail.setPayPrice(price);
        orderDetail.setMemberId(userId);
        orderDetail.setProductType(0);
        return orderDetail;
    }

    protected MerchantOrder packageOrder(Integer userId, Integer shopId, BaseOrderCreateDto orderCreateDto, String orderNo) {
        MerchantOrder order = new MerchantOrder();
        order.setOrderNo(orderNo);
        order.setUserId(userId);
        order.setShopId(shopId);
        order.setOrderType(orderCreateDto.getOrderType());
        UserDetail userDetail = this.userDetail(orderCreateDto.getAddressId(), userId);
        order.setAddress(userDetail.getAddress());
        order.setOrderStatus(OrderStatusEnums.CREATE);
        order.setPayStatus(PayStatusEnums.WAITING_PAY);
        order.setIsComment(0);
        order.setReceiveUserPhone(userDetail.getReceiveUserPhone());
        order.setReceiveUserName(userDetail.getReceiveUserName());
        order.setUsePonit(0);
        return order;
    }

    protected UserDetail userDetail(Integer addressId, Integer userId) {
        IAddressFeignClient addressFeignClient = SpringContextHolder.getBean(IAddressFeignClient.class);
        ResponseMessage<AddressDto> response;
        if (addressId != null) {
            response = addressFeignClient.findByAddressId(addressId);
        } else {
            response = addressFeignClient.findByUserIdDefault(userId);
        }
        if (response.getCode() != 200) {
            throw new ChuException("没有找到该用户的收货地址");
        }
        UserDetail userDetail = new UserDetail();
        AddressDto addressDto = response.getData();
        userDetail.setAddressId(addressDto.getId());
        userDetail.setAddress(addressDto.getAddress());
        userDetail.setUserId(userId);
        userDetail.setAddressId(addressDto.getId());
        userDetail.setReceiveUserName(addressDto.getConsignee());
        userDetail.setReceiveUserPhone(addressDto.getMobile());
        return userDetail;
    }

    public void cancelOrder(Integer memberId, String orderNo) {
        MerchantOrder order = Optional.ofNullable(orderRepository.findByUserIdAndOrderNo(memberId, orderNo)).
                orElseThrow(() -> new ChuException(String.format("订单号%s不存在", orderNo)));
        List<OrderDetail> orderDetailList = orderDetailRepository.findByOrderNo(orderNo);
        //是否已支付
        if (OrderStatusEnums.CONFIRM.equals(order.getOrderStatus()) && PayStatusEnums.WAITING_PAY.equals(order.getPayStatus())) {
            log.info("会员:{}取消订单:{}", memberId, orderNo);
            order.setOrderStatus(OrderStatusEnums.CLOSE);
            orderRepository.save(order);
            if (order.getOrderType().equals(OrderType.SECKILL)) {
                OrderDetail orderDetail = orderDetailList.get(0);
                String key  = RedisKeyPrefix.SECKILL_STOCKS.getPrefix() + orderDetail.getProductId();
                long incr = RedisUtil.incr(key, orderDetail.getQuantity());
                RedisUtil.delHash(RedisKeyPrefix.SECKILL_RECORD_USER, memberId, key);
                log.info("返回redis库存,现有库存:{}",incr);
                return;
            }
            //订单详情没有状态,可以不用处理,数据库保留订单详情记录,查询过滤掉主表订单状态即可
            //返回预留商品
            List<OrderProductReturnDto> productReturnDtoList = orderDetailList.stream().map(orderDetail -> new OrderProductReturnDto(orderDetail.getOrderNo(),
                    orderDetail.getProductId(), orderDetail.getQuantity())).collect(Collectors.toList());
            Message<String> message = MessageBuilder.withPayload(JsonUtils.obj2json(productReturnDtoList)).build();
            orderProductReturnProcessor.output().send(message);
            return;
        }
        log.info("当前用户:{},订单:{},已经支付,无法取消", memberId, orderNo);
    }


    public void payNotify(String orderNo, BigDecimal payPrice, Integer memberId) {
        MerchantOrder order = Optional.ofNullable(orderRepository.findByUserIdAndOrderNo(memberId, orderNo))
                                      .orElseThrow(() -> new ChuException("支付回调订单不存在"));
        //校验支付金额和订单金额是否一致
        if (payPrice.compareTo(order.getPayPrice()) != 0) {
            log.error("支付金额与订单金额不一致");
            return;
        }
        order.setOrderStatus(OrderStatusEnums.WAITING_SHIP);
        order.setPayStatus(PayStatusEnums.PAID);
        order = orderRepository.save(order);
        //发送广播消息订单支付成功,增加用户积分,店铺销量,物流发货等逻辑
        OrderPaySuccessDto paySuccessDto = new OrderPaySuccessDto(memberId, order.getRewardPoints(), orderNo);
        Message<OrderPaySuccessDto> message = MessageBuilder.withPayload(paySuccessDto).build();
        orderPaySuccessProcessor.output().send(message);
    }

}
